#pragma once

#include "../common/FileHelper.hpp"
#include "../common/StringHelper.hpp"
#include "../common/message.pb.h"
#include "../common/Log.hpp"
#include <string>
#include <list>
#include <unordered_map>
#include <memory>
#include <cassert>
#include <fstream>

namespace micromq
{
    using MessagePtr = std::shared_ptr<micromq::Message>;

    static const std::string dataFileNameSubFix = ".mqd";
    static const std::string tmpFileNameSubFix = ".mqd.tmp";

    class MessageMapper
    {
    public:
        MessageMapper(std::string& baseDir, const std::string& msgQueueName) : _msgQueueName(msgQueueName)
        {
            if(baseDir.back() != '/') baseDir.push_back('/');
            assert(FileHelper::createDir(baseDir));
            _dataFileName = baseDir + msgQueueName + dataFileNameSubFix;
            _tmpFileName = baseDir + msgQueueName + tmpFileNameSubFix;
            createMsgFile(_dataFileName);
        }

        void createMsgFile(const std::string& fileName)
        {
            if(!FileHelper::createFile(fileName)) 
            {
                logError("create message file error: %s", fileName.c_str());
            }
        }

        void removeMsgFile()
        {
            if(FileHelper::isExists(_dataFileName)) FileHelper::removeFile(_dataFileName);
            if(FileHelper::isExists(_tmpFileName)) FileHelper::removeFile(_tmpFileName);
        }

        void writeMessage(MessagePtr& msg)
        {
            writeMsgByFileName(_dataFileName, msg);
        }

        void eraseMessage(MessagePtr& msg)
        {
            // 将有效位置0，覆盖写入原位置
            msg->mutable_payload()->set_valid("0");
            std::string content = msg->payload().SerializeAsString();
            if(content.size() != msg->length())
            {
                logError("data inconsistencies, can't remove message..."); //数据不一致，不敢覆盖写
            }
            if(!FileHelper::write(_dataFileName, content.c_str(), msg->offset(), content.size()))
            {
                logError("remove message error...");
                return;
            }
        }

        std::list<MessagePtr> loadValidMessages()
        {
            // 加载所有有效消息
            std::list<MessagePtr> result;
            int fileSize = FileHelper::size(_dataFileName);
            int offset = 0;
            while(offset < fileSize) 
            {
                int msgLength = 0;
                if(!FileHelper::read(_dataFileName, (char*)&msgLength, offset, sizeof(msgLength)))
                {
                    logError("read message length error...");
                    return result;
                }
                offset += sizeof(msgLength);

                char content[msgLength];
                if(!FileHelper::read(_dataFileName, content, offset, msgLength))
                {
                    logError("read message content error...");
                    return result;
                }
                offset += msgLength;

                MessagePtr messagePtr = std::make_shared<Message>();
                messagePtr->mutable_payload()->ParseFromString(content);
                if(messagePtr->payload().valid() == "1")
                {
                    result.push_back(messagePtr);
                }
            }

            if(result.size() != 0) 
            {
                // 创建临时文件，将有效消息写入
                createMsgFile(_tmpFileName);
                for(auto& msg : result)
                {
                    writeMsgByFileName(_tmpFileName, msg);
                }

                // 删除原数据文件，将临时文件名改为数据文件名
                if(!FileHelper::removeFile(_dataFileName))
                {
                    logError("remove message data file error...");
                }
                if(!FileHelper::rename(_tmpFileName, _dataFileName))
                {
                    logError("rename message tmp file error...");
                }
            }

            return result;
        }

        ~MessageMapper()
        {}
    private:
        void writeMsgByFileName(const std::string& filename, MessagePtr& msg)
        {
            // 将消息写入文件，划分数据边界：消息长度|消息内容
            std::string content = msg->payload().SerializeAsString();
            int offset = FileHelper::size(filename);
            int msgLength = content.size();

            if(!FileHelper::write(filename, (char*)&msgLength, offset, sizeof(msgLength)))
            {
                logError("write message length error...");
                return;
            }
            offset += sizeof(msgLength);
            if(!FileHelper::write(filename, content.c_str(), offset, msgLength)) 
            {
                logError("write message content error...");
                return;
            }

            msg->set_offset(offset);
            msg->set_length(msgLength);
        }
    private:
        std::string _msgQueueName;
        std::string _dataFileName;
        std::string _tmpFileName;
    };
}